package za.org.jcicada.genss.sensor.katcp;

import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Set;

import za.org.jcicada.comms.impl.KatcpSerializerStrategy;
import za.org.jcicada.comms.messages.CommsMessage;
import za.org.jcicada.genss.messages.TerminateServiceInform;
import za.org.jcicada.genss.sensor.ISamplingStrategy;
import za.org.jcicada.genss.sensor.ISensor;
import za.org.jcicada.genss.sensor.ISensorType;
import za.org.jcicada.genss.sensor.SensorStatus;
import za.org.jcicada.genss.sensor.SensorURN;
import za.org.jcicada.genss.sensor.impl.Sensor;
import za.org.jcicada.genss.sensor.impl.SensorStatusValue;
import za.org.jcicada.genss.sensor.impl.StrategyDifferentialFloat;
import za.org.jcicada.genss.sensor.impl.StrategyDifferentialInt;
import za.org.jcicada.genss.sensor.impl.StrategyEvent;
import za.org.jcicada.genss.sensor.impl.StrategyNone;
import za.org.jcicada.genss.sensor.impl.StrategyPeriodic;
import za.org.jcicada.genss.sensor.impl.TypeBoolean;
import za.org.jcicada.genss.sensor.impl.TypeDiscrete;
import za.org.jcicada.genss.sensor.impl.TypeFloat;
import za.org.jcicada.genss.sensor.impl.TypeInt;
import za.org.jcicada.genss.sensor.impl.TypeLRU;
import za.org.jcicada.genss.sensor.messages.AttachReply;
import za.org.jcicada.genss.sensor.messages.AttachRequest;
import za.org.jcicada.genss.sensor.messages.DetachReply;
import za.org.jcicada.genss.sensor.messages.DetachRequest;
import za.org.jcicada.genss.sensor.messages.SensorSetReply;
import za.org.jcicada.genss.sensor.messages.SensorSetRequest;
import za.org.jcicada.genss.sensor.messages.SetSamplingStrategyReply;
import za.org.jcicada.genss.sensor.messages.SetSamplingStrategyRequest;
import za.org.jcicada.genss.sensor.messages.UpdateSensorInform;
import za.org.jcicada.genss.sensor.messages.UpdateSensorStatusInform;
import za.org.jcicada.genss.sensor.messages.UpdateSensorTypeInform;

public class SensorKatcpSerializer extends KatcpSerializerStrategy {

    public SensorKatcpSerializer() {
        super(new Marshal(), new Unmarshal());
    }
    
    public static class Marshal {
        public Object[] getSetURN(Set<SensorURN> urns) {
            List<String> uriList = new ArrayList<String>();
            for (SensorURN uri: urns) {
                uriList.add(uri.toString());
            }
            return uriList.toArray(new Object[0]);
        }
        
        public Object[] AttachReply(AttachReply msg) {
        	Object[] args = new Object[] { msg.getCode(), msg.getMessage() };
            return concatenate(args, getSetURN(msg.getSensorURNs()));
        }
        
        public Object[] AttachRequest(AttachRequest msg) {
            return getSetURN(msg.getSensorURNs());
        }
        
        public Object[] DetachReply(DetachReply msg) {
        	Object[] args = new Object[] { msg.getCode(), msg.getMessage() };
            return concatenate(args, getSetURN(msg.getSensorURNs()));
        }
    
        public Object[] DetachRequest(DetachRequest msg) {
            return getSetURN(msg.getSensorURNs());
        }
            
        public Object[] SensorSetReply(SensorSetReply msg) {
            return new Object[] { msg.getCode(), msg.getMessage(), Integer.toString(msg.getNumSensors())};
        }
    
        public Object[] SensorSetRequest(SensorSetRequest msg) {
            return new Object[] { };
        }
        
        public Object[] SetSamplingStrategyReply(SetSamplingStrategyReply msg) {
            return new Object[] { msg.getCode(), msg.getMessage() };
        }
        
        public Object[] SetSamplingStrategyRequest(SetSamplingStrategyRequest msg) {
            String[] composition = msg.getSamplingStrategy().getClass().getCanonicalName().split("\\.");
            String[] arr1 = new String[]{composition[composition.length-1]};
            String[] arr2 = msg.getSamplingStrategy().toStringArray();
            return concatenate(arr1, arr2);
        }
        
        public Object[] TerminateServiceInform(TerminateServiceInform msg) {
            return new Object[] { msg.getServiceURN() };
        }
            
        public Object[] UpdateSensorInform(UpdateSensorInform msg) {
            return new Object[]{msg.getSensorURN(), Long.toString(msg.getValue().getTime()), msg.getValue().getValue() };
        }
        
        public Object[] UpdateSensorStatusInform(za.org.jcicada.genss.sensor.messages.UpdateSensorStatusInform msg) {
        	return new Object[]{msg.getSensorURN(), Long.toString(msg.getStatusValue().getTime()), msg.getStatusValue().getStatus(), msg.getStatusValue().getMessage() };
        }
        
        public Object[] UpdateSensorTypeInform(UpdateSensorTypeInform msg) {
            String[] composition = msg.getSensor().getType().getClass().getCanonicalName().split("\\.");
            String[] arr1 = new String[]{msg.getSensorURN().toString(), msg.getSensor().getName(), msg.getSensor().getDescription(), msg.getSensor().getUnits(), composition[composition.length-1]};
            String[] arr2 = msg.getSensor().getType().toStringArray();
            return concatenate(arr1, arr2);
        }
        
        public final Object [] concatenate(final Object [] first, final Object [] second) {
            // Assuming non-null for brevity.
            final ArrayList<Object> resultList = new ArrayList<Object>(Arrays.asList(first));
            resultList.addAll(new ArrayList<Object>(Arrays.asList(second)));
            return resultList.toArray(new Object [resultList.size()]);
        }
    }
    
    public static class Unmarshal {
        
        public CommsMessage AttachReply(String[] args) {
            return new AttachReply(parseResult(args), getSetSensorURNs(args, 2));
        }
        
        public CommsMessage AttachRequest(String[] args) {
            return new AttachRequest(getSetSensorURNs(args, 0));
        }
        
        public CommsMessage DetachReply(String[] args) {
            return new DetachReply(parseResult(args), getSetSensorURNs(args, 2));
        }
    
        public CommsMessage DetachRequest(String[] args) {
            return new DetachRequest(getSetSensorURNs(args, 0));
        }
            
        public CommsMessage SensorSetReply(String[] args) {
            return new SensorSetReply(parseResult(args), Integer.parseInt(args[2]));
        }
    
        public CommsMessage SensorSetRequest(String[] args) {
            return new SensorSetRequest();
        }
        
        public CommsMessage SetSamplingStrategyReply(String[] args) {
            return new SetSamplingStrategyReply(parseResult(args));
        }
        
        public CommsMessage SetSamplingStrategyRequest(String[] args) throws URISyntaxException {
            String[] strategyArgs = new String[args.length-1];
            System.arraycopy(args, 1, strategyArgs, 0, strategyArgs.length);
            ISamplingStrategy strategy = null;
            if (args[0].equals("StrategyDifferentialFloat")) {
                strategy = new StrategyDifferentialFloat(strategyArgs);
            } else if (args[0].equals("StrategyDifferentialInt")) {
                strategy = new StrategyDifferentialInt(strategyArgs);
            } else if (args[0].equals("StrategyEvent")) {
                strategy = new StrategyEvent(strategyArgs);
            } else if (args[0].equals("StrategyNone")) {
                strategy = new StrategyNone(strategyArgs);
            } else if (args[0].equals("StrategyPeriodic")) {
                strategy = new StrategyPeriodic(strategyArgs);
            }
            return new SetSamplingStrategyRequest(strategy);
        }
        
        public CommsMessage TerminateServiceInform(String[] args) {
            return new TerminateServiceInform(parseServiceURN(args, 0));
        }
        
        public CommsMessage UpdateSensorStatusInform(String[] args) {
        	long time = Long.parseLong(args[1]);
        	SensorStatus status = SensorStatus.valueOf(args[2]);
        	String message = args[3];
        	return new UpdateSensorStatusInform(parseSensorURN(args, 0), new SensorStatusValue(status, time, message));
        }
            
        public CommsMessage UpdateSensorTypeInform(String[] args) {
            String[] typeArgs = new String[args.length-5];
            System.arraycopy(args, 5, typeArgs, 0, typeArgs.length);
            SensorURN sensorURN = parseSensorURN(args, 0);
            ISensorType type = null;
            if (args[4].equals("TypeBoolean")) {
                type = TypeBoolean.getInstance(typeArgs);
            } else if (args[4].equals("TypeDiscrete")) {
                type = TypeDiscrete.getInstance(typeArgs);
            } else if (args[4].equals("TypeFloat")) {
                type = TypeFloat.getInstance(typeArgs);
            } else if (args[4].equals("TypeInt")) {
                type = TypeInt.getInstance(typeArgs);
            } else if (args[4].equals("TypeLRU")) {
                type = TypeLRU.getInstance(typeArgs);
            }
            ISensor sensor = new Sensor(type, args[1], args[2], args[3]);
            return new UpdateSensorTypeInform(sensorURN, sensor);
        }
        
    }
    
}
